Imports Clo = Enhancements.Core.Logging
Imports Ere = Enhancements.Basic.RegularExpressions
Imports Gen = System.Collections.Generic
Imports Glo = System.Globalization
Imports Iou = System.IO
Imports Pri = Enhancements.Basic.Primitives
Imports Ref = System.Reflection
Imports Reg = System.Text.RegularExpressions
Imports Sys = System
Imports Tex = System.Text
Imports Xsd = System.Xml.Schema
Imports W3c = System.Xml
Imports Vbn = Microsoft.VisualBasic

Namespace Enhancements.Basic.Expressions.Values
    ''' <summary>Represents a token extracted from an expression that has a string value.</summary>
    ''' <remarks>A string value must be delimited by the value defined by the DelimiterRepresentation property. Also the given value must obey to the limits imposed by the MinimumLength and MaximumLength properties. Special inclusions can be made throughout a string by prefixing them with the value defined by the CharacterSpecifierRepresentation property. Following that the user can put a \ character to signify its occurrence, or a three-digit number ranging from 000 to 127 signifying an ASCII character code.</remarks>
    <Serializable(), DebuggerStepThrough()> _
    Public NotInheritable Class [String]
        Inherits Value

        Private Sub New(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32, ByVal _expressionEndIndex As Sys.Int32, ByVal _value As System.String)
            MyBase.New(_expression, _expressionBeginIndex, _expressionEndIndex, _value)
        End Sub

        ''' <summary>The associated framework type used for the token value storage.</summary>
        Public Shared ReadOnly Property UnderlyingType() As Sys.Type
            Get
                Dim _returnValue As Sys.Type = GetType(System.String)

                Return _returnValue
            End Get
        End Property

        ''' <summary>The delimiter these tokens representations must begin and end with.</summary>
        Public Shared ReadOnly Property DelimiterRepresentation() As System.String
            Get
                Dim _returnValue As System.String = """"

                Return _returnValue
            End Get
        End Property

        ''' <summary>The prefix that must be used before special characters are specified.</summary>
        Public Shared ReadOnly Property CharacterSpecifierRepresentation() As System.String
            Get
                Dim _returnValue As System.String = "\"

                Return _returnValue
            End Get
        End Property

        ''' <summary>The minimum length accepted for a string token.</summary>
        Public Shared ReadOnly Property MinimumLength() As System.Int32
            Get
                Dim _returnValue As System.Int32 = 0

                Return _returnValue
            End Get
        End Property

        ''' <summary>The maximum length accepted for a string token.</summary>
        Public Shared ReadOnly Property MaximumLength() As System.Int32
            Get
                Dim _returnValue As System.Int32 = System.Int32.MaxValue - DelimiterRepresentation.Length * 2

                Return _returnValue
            End Get
        End Property

        Private Shared Function __Parse(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32) As Sys.Object
            Dim _returnValue As Sys.Object = Nothing
            Dim _returnValueIsPossible As System.Boolean = True
            If _expression.IsNullNot("_expression", False) AndAlso _expression.IsEmptyNot("_expression", False) AndAlso _expressionBeginIndex.IsInRange("_expressionBeginIndex", 0, _expression.Length - 1, False) Then
                If ___Texts.StartsWith(_expression.Substring(_expressionBeginIndex), DelimiterRepresentation) Then
                    If ___Texts.IndexOf(_expression, DelimiterRepresentation, _expressionBeginIndex + DelimiterRepresentation.Length) <> -1 Then
                        Dim _returnValueStringBuilder As Tex.StringBuilder = New Tex.StringBuilder(Sys.String.Empty)
                        Dim _expressionSearchBeginIndex As Sys.Int32 = _expressionBeginIndex + DelimiterRepresentation.Length
                        Dim _expressionSearchEndIndex As Sys.Int32 = ___Texts.IndexOf(_expression, DelimiterRepresentation, _expressionBeginIndex + DelimiterRepresentation.Length) - 1
                        For _counter As Sys.Int32 = _expressionSearchBeginIndex To _expressionSearchEndIndex
                            If ___Texts.StartsWith(_expression.Substring(_counter), CharacterSpecifierRepresentation) Then
                                If ___Texts.StartsWith(_expression.Substring(_counter + CharacterSpecifierRepresentation.Length), CharacterSpecifierRepresentation) Then
                                    _returnValueStringBuilder.Append(CharacterSpecifierRepresentation)
                                    _counter += CharacterSpecifierRepresentation.Length * 2 - 1
                                Else
                                    If _counter + CharacterSpecifierRepresentation.Length + 3 > _expression.Length Then
                                        _returnValueIsPossible = False
                                        Exit For
                                    Else
                                        Dim _asciiCharacterNumberString As System.String = _expression.Substring(_counter + CharacterSpecifierRepresentation.Length, 3)
                                        If _asciiCharacterNumberString.Length <> 3 Then
                                            _returnValueIsPossible = False
                                            Exit For
                                        Else
                                            If (Not Sys.Char.IsDigit(_asciiCharacterNumberString.Chars(0))) Or (Not Sys.Char.IsDigit(_asciiCharacterNumberString.Chars(1))) Or (Not Sys.Char.IsDigit(_asciiCharacterNumberString.Chars(2))) Then
                                                _returnValueIsPossible = False
                                                Exit For
                                            Else
                                                Dim _asciiCharacterNumberInteger As Sys.Int32 = 0
                                                If Not Sys.Int32.TryParse(_asciiCharacterNumberString, _asciiCharacterNumberInteger) Then
                                                    _returnValueIsPossible = False
                                                    Exit For
                                                Else
                                                    If _asciiCharacterNumberInteger.IsInRangeNot("_asciiCharacterNumberInteger", 0, 127, False) Then
                                                        _returnValueIsPossible = False
                                                        Exit For
                                                    Else
                                                        _returnValueStringBuilder.Append(Vbn.Chr(_asciiCharacterNumberInteger))
                                                        _counter += 3
                                                    End If
                                                End If
                                            End If
                                        End If
                                    End If
                                End If
                            Else
                                _returnValueStringBuilder.Append(_expression.Chars(_counter))
                            End If
                        Next _counter
                        If _returnValueIsPossible.IsTrue("_returnValueIsNotPossible", False) Then
                            If _returnValueStringBuilder.ToString().Length.IsInRangeNot("_returnValueStringBuilder.ToString().Length", MinimumLength, MaximumLength, False) Then
                                _returnValueIsPossible = False
                            End If
                        End If
                        If _returnValueIsPossible.IsTrue("_returnValueIsNotPossible", False) Then
                            _returnValue = New [String](_expression, _expressionSearchBeginIndex - DelimiterRepresentation.Length, _expressionSearchEndIndex + DelimiterRepresentation.Length, _returnValueStringBuilder.ToString())
                        End If
                    End If
                End If
            End If
            If Assert.IsNull(_returnValue, "_returnValue", False) Then
                _returnValue = StringParseException.___Create(_expression, _expressionBeginIndex)
            End If

            Return _returnValue
        End Function

        ''' <summary>Parses an expression for a string value starting by a begin index.</summary>
        ''' <param name="_expression">Expression to parse.</param>
        ''' <param name="_expressionBeginIndex">Expression begin index to use.</param>
        ''' <exception cref="StringParseException">The operation did not produce a valid token.</exception>
        ''' <returns> </returns>
        Public Overloads Shared Function Parse(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32) As [String]
            Dim _returnValue As Sys.Object = __Parse(_expression, _expressionBeginIndex)
            If Assert.IsOfType(_returnValue, "_returnValue", GetType(StringParseException), False) Then
                StringParseException.___Throw(CType(_returnValue, StringParseException))
            End If

            Return CType(_returnValue, [String])
        End Function

        ''' <summary>Tries to parse an expression for a string value starting by a begin index.</summary>
        ''' <param name="_expression">Expression to parse.</param>
        ''' <param name="_expressionBeginIndex">Expression begin index to use.</param>
        ''' <param name="_result">The result of the operation if possible. If not, null is returned.</param>
        ''' <returns> </returns>
        Public Overloads Shared Function TryParse(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32, ByRef _result As [String]) As System.Boolean
            Dim _returnValue As System.Boolean = False
            Dim _dummy As Sys.Object = __Parse(_expression, _expressionBeginIndex)
            If Assert.IsOfType(_dummy, "_dummy", GetType(StringParseException), False) Then
                _result = Nothing
                _returnValue = False
            Else
                _result = CType(_dummy, [String])
                _returnValue = True
            End If

            Return _returnValue
        End Function

        Friend Overloads Shared Function ___AssertIsValidValue(ByVal _value As Sys.Object) As System.Boolean
            Dim _returnValue As System.Boolean = False
            If Assert.IsNullNot(_value, "_value", False) Then
                If Assert.IsOfType(_value, "_value", [String].UnderlyingType, False) Then
                    Dim _valueString As System.String = CType(_value, System.String)
                    If _valueString.Length.IsInRange("_valueString.Length", MinimumLength, MaximumLength, False) Then
                        _returnValue = True
                    End If
                End If
            End If

            Return _returnValue
        End Function

        ''' <summary>Determines the token's correct representation for use in an expression.</summary>
        ''' <returns> </returns>
        Public Overrides Function ToRepresentation() As System.String
            Dim _returnValue As System.String = __ToRepresentation(CType(Value, System.String))

            Return _returnValue
        End Function

        ''' <summary>Determines the correct representation of a string value for use in an expression.</summary>
        ''' <exception cref="StringRepresentationException">The operation did not produce a valid representation because of the input value nature.</exception>
        ''' <returns> </returns>
        Public Overloads Shared Function ToRepresentation(ByVal _value As System.String) As System.String
            If Not ___AssertIsValidValue(_value) Then
                StringRepresentationException.___Throw(_value)
            End If

            Dim _returnValue As System.String = __ToRepresentation(_value)

            Return _returnValue
        End Function

        Private Shared Function __ToRepresentation(ByVal _value As System.String) As System.String
            Dim _returnValueStringBuilder As Tex.StringBuilder = New Tex.StringBuilder(Sys.String.Empty)
            _returnValueStringBuilder.Append(DelimiterRepresentation)
            For _counter As Sys.Int32 = 0 To _value.Length - 1
                If ___Texts.StartsWith(_value.Substring(_counter), CharacterSpecifierRepresentation) Then
                    _returnValueStringBuilder.Append(CharacterSpecifierRepresentation + CharacterSpecifierRepresentation)
                    _counter += CharacterSpecifierRepresentation.Length - 1
                Else
                    If Vbn.Asc(_value.Chars(_counter)).IsInRange("Vbn.Asc(_value.Chars(_counter))", 0, 31, False) OrElse Vbn.Asc(_value.Chars(_counter)).IsInRange("Vbn.Asc(_value.Chars(_counter))", 127, 127, False) Then
                        _returnValueStringBuilder.Append(CharacterSpecifierRepresentation)
                        _returnValueStringBuilder.Append(Vbn.Asc(_value.Chars(_counter)).ToString(Glo.CultureInfo.InvariantCulture).PadLeft(3, "0"c))
                    Else
                        _returnValueStringBuilder.Append(_value.Chars(_counter))
                    End If
                End If
            Next _counter
            _returnValueStringBuilder.Append(DelimiterRepresentation)

            Return _returnValueStringBuilder.ToString()
        End Function

        ''' <exception cref="IsNullException"></exception>
        ''' <exception cref="CompilerExpressionStringsDelimitersCountIsValidNotException"></exception>
        ''' <exception cref="CompilerExpressionStringsAreValidNotException"></exception>
        Friend Shared Function ___ParseAll(ByVal _token As Undetermined) As Token()
            _token.IsNullNot("_token", True)
            Dim _delimiterRepresentationOccurrences() As Pri.Occurrence = Pri.String.FindOccurrences(___Texts.ToUpper(_token.Expression), ___Texts.ToUpper(DelimiterRepresentation))
            If Assert.IsInRangeNot(_delimiterRepresentationOccurrences.Length Mod 2, "_delimiterRepresentationOccurrences.Length Mod 2", 0, 0, False) Then
                CompilerExpressionStringsDelimitersCountIsValidNotException.___Throw(_token.Expression, _delimiterRepresentationOccurrences.Length)
            Else
                If Assert.IsInRange(_delimiterRepresentationOccurrences.Length, "_delimiterRepresentationOccurrences.Length", 2, Sys.Int32.MaxValue, False) Then
                    For _counter As Sys.Int32 = 0 To _delimiterRepresentationOccurrences.Length - 1 Step 2
                        Dim _possibleString As System.String = _token.Expression.Substring(_delimiterRepresentationOccurrences(_counter).BeginIndex, _delimiterRepresentationOccurrences(_counter + 1).EndIndex - _delimiterRepresentationOccurrences(_counter).BeginIndex + 1)
                        Dim _possibleStringToken As [String] = Nothing
                        If TryParse(_possibleString, 0, _possibleStringToken).IsTrueNot("TryParse(_possibleString, 0, _possibleStringToken)", False) Then
                            CompilerExpressionStringsAreValidNotException.___Throw(_token.Expression, _possibleString)
                        Else
                            If _possibleStringToken.ExpressionPart.Length.IsInRangeNot("_possibleStringToken.ExpressionPart.Length", _delimiterRepresentationOccurrences(_counter + 1).EndIndex - _delimiterRepresentationOccurrences(_counter).BeginIndex + 1, _delimiterRepresentationOccurrences(_counter + 1).EndIndex - _delimiterRepresentationOccurrences(_counter).BeginIndex + 1, False) Then
                                CompilerExpressionStringsAreValidNotException.___Throw(_token.Expression, _possibleString)
                            End If
                        End If
                    Next _counter
                End If
            End If

            Dim _returnValueList As Gen.List(Of Token) = New Gen.List(Of Token)()
            For _counter As Sys.Int32 = 0 To _token.Expression.Length - 1
                Dim _string As [String] = Nothing
                If TryParse(_token.Expression, _counter, _string) Then
                    _returnValueList.Add(_string)
                    _counter += _string.ExpressionPart.Length - 1
                Else
                    Undetermined.___AppendNewToEndOrReplaceLastWithNew(_returnValueList, _token.Expression, _counter, _counter)
                End If
            Next _counter

            Return _returnValueList.ToArray()
        End Function

    End Class
End Namespace
